using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using MobMentality.Map;
using MobMentality.Social;
using System.Threading;

namespace MobMentality
{
    /*Main Game Class*/
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
		SpriteFont gameFont;

        bool hasStarted = false;
        bool IsServer = false;

		//Input Variables
		bool mouseClicked = false;
		bool mPressed = false;
		bool rPressed = false;
		bool hPressed = false;
		bool spacePressed = false;

        //Network Input Variables
        bool n_mouseClicked = false;
        bool n_mPressed = false;
        bool n_rPressed = false;
        bool n_hPressed = false;

		//DEBUG VARIABLES
		int framesThisSecond = 0;
		int framesLastSecond = 0;
		TimeSpan framerateElapsedTime = TimeSpan.Zero;

		//DEMO VARIABLES
		Vector2 Camera = Vector2.Zero;

		IMapDraw drawMap;
		IMapNavigate navigationMap;
		IGroupController unconverted;
		IGroupController userGroup1;
		IGroupController userGroup2;
		IGroupController copsGroup1;
		IGroupController copsGroup2;
		IGroupController copsGroup3;
		IGroupController copsGroup4;
		IGroupController copsGroup5;
		IGroupController selectedGroup;
        
        IGroupController n_selectedGroup;

        HUD hud;

        Network.Client client;
        Network.Server server;

        Thread t;

        
		/*Initialize Class Variables*/
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);

			/*SET RESOLUTION*/
			this.graphics.PreferredBackBufferWidth = 1280;
			this.graphics.PreferredBackBufferHeight = 800;

			/*SET TO FULLSCREEN*/
			//this.graphics.IsFullScreen = true;

            Content.RootDirectory = "Content";

            this.Exiting += new EventHandler<System.EventArgs>(Game1_Exiting);
        }

        void Game1_Exiting(object sender, EventArgs e)
        {
            if (IsServer)
            {
                t.Abort();
            }
        }

        /*Initialize Game Variables*/
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            base.Initialize();
			this.IsMouseVisible = true;
        }

        protected void setupNetwork()
        {
            if (IsServer)
            {
                server = new Network.Server();
                t = new Thread(new ThreadStart(server.ServerRun));
                t.Start();
            }
            else
            {
                client = new Network.Client();
                client.DiscoverPeers();
            }

        }

        /* Load Sprites/Maps/Music/Etc. */
		protected override void LoadContent()
		{
            Texture2D hudBG;

			// Create a new SpriteBatch, which can be used to draw textures.
			spriteBatch = new SpriteBatch(GraphicsDevice);

			//Load Fonts
			gameFont = Content.Load<SpriteFont>("GameFont");

			//Load Images
            hudBG = Content.Load<Texture2D>("HUD_back");        //BIZARRO, I'M HELPING
            

			PeopleHolder.Instance.Initialize(Content);

			/*Get Maps*/
			drawMap = MapBuilder.getMapDraw("foo.xml", Content, 1280, 800);
			navigationMap = MapBuilder.getMapNavigate("foo.xml");

			/*Tell Navigator about our new navigationMap*/
			PersonNavigator.Instance.NavigationMap = navigationMap;

            

            /*Make the "Populace" group, people who aren't in any other group*/
            unconverted = GroupControllerFactory.MakeGroupController("Unconverted", Color.LightGray, navigationMap, Vector2.Zero, Team.Unconverted, Content);

            userGroup1 = GroupControllerFactory.MakeGroupController("Converted1", Color.Red, navigationMap, navigationMap.GetMapPosition(new Point(1, 2), new Vector2(100, 100)), Team.Riot, Content);

            userGroup2 = GroupControllerFactory.MakeGroupController("Converted2", Color.Yellow, navigationMap, navigationMap.GetMapPosition(new Point(1, 1), new Vector2(100, 100)), Team.Riot, Content);

            copsGroup1 = GroupControllerFactory.MakeGroupController("Cops1", Color.Blue, navigationMap, navigationMap.GetMapPosition(new Point(5, 4), new Vector2(100, 100)), Team.Police, Content);

            copsGroup2 = GroupControllerFactory.MakeGroupController("Cops2", Color.Aqua, navigationMap, navigationMap.GetMapPosition(new Point(12, 1), new Vector2(100, 100)), Team.Police, Content);

            copsGroup3 = GroupControllerFactory.MakeGroupController("Cops3", Color.Turquoise, navigationMap, navigationMap.GetMapPosition(new Point(1, 9), new Vector2(100, 100)), Team.Police, Content);
            
            copsGroup4 = GroupControllerFactory.MakeGroupController("Cops4", Color.CadetBlue, navigationMap, navigationMap.GetMapPosition(new Point(12, 9), new Vector2(100, 100)), Team.Police, Content);

            copsGroup5 = GroupControllerFactory.MakeGroupController("Cops5", Color.CornflowerBlue, navigationMap, navigationMap.GetMapPosition(new Point(7, 12), new Vector2(100, 100)), Team.Police, Content);
            
            /* Initialize HUD */
            hud = new HUD(Content);

			/*User hasn't selected anyone yet*/
			selectedGroup = null;

			
			int numToSpawn = 6;

			/*UNCONVERTED POPULACE*/
			for (int i = 0; i < numToSpawn; i++)
			{
				Vector2 Position = new Vector2((float)Math.Cos(i * 2 * Math.PI/numToSpawn), (float)Math.Sin(i * 2 * Math.PI/numToSpawn));
				Position *= 40;
				Position += new Vector2(100, 100);
				PeopleHolder.Instance.SpawnPeople(1, unconverted, navigationMap.GetMapPosition(new Point(1, 1), Position));
				PeopleHolder.Instance.SpawnPeople(1, unconverted, navigationMap.GetMapPosition(new Point(7, 6), Position));
				PeopleHolder.Instance.SpawnPeople(1, unconverted, navigationMap.GetMapPosition(new Point(13, 5), Position));
				PeopleHolder.Instance.SpawnPeople(1, unconverted, navigationMap.GetMapPosition(new Point(1, 13), Position));
			}

			/*COPS*/
			numToSpawn = 3;
			for (int i = 0; i < numToSpawn; i++)
			{
				Vector2 Position = new Vector2((float)Math.Cos(i * 2 * Math.PI / numToSpawn), (float)Math.Sin(i * 2 * Math.PI / numToSpawn));
				Position *= 40;
				Position += new Vector2(100, 100);
				PeopleHolder.Instance.SpawnPeople(1, copsGroup1, navigationMap.GetMapPosition(new Point(5, 4), Position));
			}

			for (int i = 0; i < numToSpawn; i++)
			{
				Vector2 Position = new Vector2((float)Math.Cos(i * 2 * Math.PI / numToSpawn), (float)Math.Sin(i * 2 * Math.PI / numToSpawn));
				Position *= 40;
				Position += new Vector2(100, 100);
				PeopleHolder.Instance.SpawnPeople(1, copsGroup2, navigationMap.GetMapPosition(new Point(12, 1), Position));
			}

			for (int i = 0; i < numToSpawn; i++)
			{
				Vector2 Position = new Vector2((float)Math.Cos(i * 2 * Math.PI / numToSpawn), (float)Math.Sin(i * 2 * Math.PI / numToSpawn));
				Position *= 40;
				Position += new Vector2(100, 100);
				PeopleHolder.Instance.SpawnPeople(1, copsGroup3, navigationMap.GetMapPosition(new Point(1, 9), Position));
			}

			for (int i = 0; i < numToSpawn; i++)
			{
				Vector2 Position = new Vector2((float)Math.Cos(i * 2 * Math.PI / numToSpawn), (float)Math.Sin(i * 2 * Math.PI / numToSpawn));
				Position *= 40;
				Position += new Vector2(100, 100);
				PeopleHolder.Instance.SpawnPeople(1, copsGroup4, navigationMap.GetMapPosition(new Point(12, 9), Position));
			}

			for (int i = 0; i < numToSpawn; i++)
			{
				Vector2 Position = new Vector2((float)Math.Cos(i * 2 * Math.PI / numToSpawn), (float)Math.Sin(i * 2 * Math.PI / numToSpawn));
				Position *= 40;
				Position += new Vector2(100, 100);
				PeopleHolder.Instance.SpawnPeople(1, copsGroup5, navigationMap.GetMapPosition(new Point(7, 12), Position));
			}
			
		}

        /*Clean up*/
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

		/*Update*/
        protected override void Update(GameTime gameTime)
        {
            
            //take user input
            Input();

            if (hasStarted)
            {
                if (IsServer)
                {
                    //process network input
                    ProcessNet();
                }

                if (!IsServer)
                {
                    client.ReadMessages();
                }
            }

            if (IsServer)
            {
                PeopleHolder.Instance.UpdatePeople();

                //unconverted.UpdateGoals(false);
                userGroup1.UpdateGoals(true);
                userGroup2.UpdateGoals(true);

                copsGroup1.UpdateGoals(true);
                copsGroup2.UpdateGoals(true);
                copsGroup3.UpdateGoals(true);
                copsGroup4.UpdateGoals(true);
                copsGroup5.UpdateGoals(true);
            }
            else
            {
                
            }

            //DEBUG CODE
            framerateElapsedTime += gameTime.ElapsedGameTime;

            if (framerateElapsedTime > TimeSpan.FromSeconds(1))
            {
                framerateElapsedTime -= TimeSpan.FromSeconds(1);
                framesLastSecond = framesThisSecond;
                framesThisSecond = 0;
            }

            if (navigationMap.CheckRiotControlpointEndgame())
            {
                Console.Out.WriteLine("RIOT WIN!!!");
            }

            if (PeopleHolder.Instance.CheckChaosEndgame())
            {
                Console.Out.WriteLine("CHAOS!!!");
            }

            if (PeopleHolder.Instance.CheckSupressedEndgame())
            {
                Console.Out.WriteLine("COP WIN!!!");
            }

            

            // TODO: Add your update logic here

            base.Update(gameTime);
        }

        /*Draw*/
        protected override void Draw(GameTime gameTime)
        {
            if (hasStarted)
            {
                GraphicsDevice.Clear(Color.CornflowerBlue);

                spriteBatch.Begin();

                drawMap.Draw(Camera, spriteBatch, navigationMap.GetCapturedTiles());
                PeopleHolder.Instance.Draw(Camera, spriteBatch);

                hud.Draw(spriteBatch);

                //DEBUG CODE
                //display framerate
                int left = 1100;

                string fps = string.Format("fps: {0}", framesLastSecond);
                spriteBatch.DrawString(gameFont, fps, new Vector2(left, 5), Color.Yellow);

                //display selected group
                string group;
                if (selectedGroup != null)
                {
                    group = string.Format("group: {0}", selectedGroup.GroupName);
                    hud.select(selectedGroup);
                    hud.selectPerson();
                }
                else
                {
                    hud.clear();
                    group = "No Group";
                }
                spriteBatch.DrawString(gameFont, group, new Vector2(left, 25), Color.Yellow);

                if (selectedGroup != null && selectedGroup.IsInciterSelected)
                {
                    spriteBatch.DrawString(gameFont, "Inciter Selected", new Vector2(left, 45), Color.Yellow);
                    hud.selectInciter();
                }

                spriteBatch.End();

                // TODO: Add your drawing code here

                base.Draw(gameTime);

                framesThisSecond++;
            }
            else
            {
                //TODO: TELL USER PRESS S FOR SERVER, C FOR CLIENT
            }
        }

        private void ProcessNet()
        {
            while (server.net_cmd.Count != 0)
            {
                NetInput(server.net_cmd.Dequeue());
            }
        }

        /*
         * Get/Handle NetworkInput
         */

        private void NetInput(Vector2 rem)
        {
            /*M: add melee person*/
            if (!n_mPressed && rem.Equals(new Vector2(-666, 1)))
            {
                if (n_selectedGroup != null)
                {
                    n_selectedGroup.AddPerson(PersonType.Melee);
                    n_selectedGroup = null;
                }
                n_mPressed = true;
            }
            if (n_mPressed && rem.Equals(new Vector2(-666, 1)))
            {
                n_mPressed = false;
            }

            /*R: add range person*/
            if (!n_rPressed && rem.Equals(new Vector2(-666, 2)))
            {
                if (n_selectedGroup != null)
                {
                    n_selectedGroup.AddPerson(PersonType.Range);
                    n_selectedGroup = null;
                }
                n_rPressed = true;
            }
            if (n_rPressed && rem.Equals(new Vector2(-666, 2)))
            {
                n_rPressed = false;
            }

            /*H: add healer person*/
            if (!n_hPressed && rem.Equals(new Vector2(-666, 3)))
            {
                if (n_selectedGroup != null)
                {
                    n_selectedGroup.AddPerson(PersonType.Healer);
                    n_selectedGroup = null;
                }
                n_hPressed = true;
            }
            if (n_hPressed && rem.Equals(new Vector2(-666, 3)))
            {
                n_hPressed = false;
            }

            //Mouse Clicks
            if (!n_mouseClicked)
            {
                if (rem.X>0 && rem.Y>0)
                {
                    //if no group is currently n_selected
                    if (n_selectedGroup == null)
                    {
                        IGroupController clickedGroup = PeopleHolder.Instance.SelectGroup(rem, 15);
                        if (clickedGroup != null)
                        {
                            Console.WriteLine("Network: " + clickedGroup.Team);
                        }
                        //if a group was clicked on
                        if (clickedGroup != null)
                        {
                            //tell the group, if the group returns true, globally select the group
                            if (clickedGroup.OnNetClick(rem))
                            {
                                n_selectedGroup = clickedGroup;
                            }
                            else //otherwise move on.
                            {
                                n_selectedGroup = null;
                            }
                        }
                    }
                    else //a group is already n_selected, do something with them
                    {
                        n_selectedGroup.GoalLocation(rem);
                        n_selectedGroup = null;
                    }
                    n_mouseClicked = true;
                }
            }
            else
            {
                //if (mouse.LeftButton == ButtonState.Released)
                //{
                    n_mouseClicked = false;
                //}
            }
        }


		/*
		 * Get/Handle User Input
		 */
		private void Input()
		{
			KeyboardState keyboard = Keyboard.GetState();
			MouseState mouse = Mouse.GetState();

			//do shit with keyboard and mouse

			//exit on ESC
			if(keyboard.IsKeyDown(Keys.Escape))
			{
				this.Exit();
			}

			/*
			//only move the screen if the mouse is in the window
			if (mouse.X > 0 && mouse.X < Window.ClientBounds.Width && mouse.Y > 0 && mouse.Y < Window.ClientBounds.Height)
			{
				*TODO: Put this in a class or something*
				float threshold = 200;
				float scaleFactor = 25;
				Vector2 moveCamera = new Vector2(mouse.X, mouse.Y) - new Vector2(Window.ClientBounds.Width / 2, Window.ClientBounds.Height / 2);

				if (moveCamera.Length() > threshold)
				{
					Camera += (moveCamera / scaleFactor);
				}
			}
			*/

			float keyboardMoveSpeed = 10;

            if (keyboard.IsKeyDown(Keys.S) && !hasStarted)
            {
                hasStarted = true;
                IsServer = true;
                setupNetwork();
            }
            else if (keyboard.IsKeyDown(Keys.C) && !hasStarted)
            {
                hasStarted = true;
                IsServer = false;
                setupNetwork();
            }

            if (IsActive)
            {
                if (keyboard.IsKeyDown(Keys.W))
                {
                    Camera.Y -= keyboardMoveSpeed;
                }
                if (keyboard.IsKeyDown(Keys.S))
                {
                    Camera.Y += keyboardMoveSpeed;
                }
                if (keyboard.IsKeyDown(Keys.A))
                {
                    Camera.X -= keyboardMoveSpeed;
                }
                if (keyboard.IsKeyDown(Keys.D))
                {
                    Camera.X += keyboardMoveSpeed;
                }

                if (keyboard.IsKeyDown(Keys.B))
                {
                    if (IsServer)
                    {
                        //server.SendV4(server.server, server.other, new Vector4(1, 2, 3, 4));
                        List<Vector4> gv = PeopleHolder.Instance.GenerateVectorList();
                        foreach (Vector4 vp in gv)
                        {
                            server.SendV4(server.server, server.other, vp);
                        }
                    }
                    
                }

                /*M: add melee person*/
                if (!mPressed && keyboard.IsKeyDown(Keys.M))
                {
                    if (!IsServer)
                    {
                        //each keypress is sent as a vector for the sake of simplicity in reading data
                        // M = 1
                        client.SendLocation(new Vector2(-666, 1));
                    }
                    if (selectedGroup != null)
                    {
                        selectedGroup.AddPerson(PersonType.Melee);
                        selectedGroup = null;
                    }
                    mPressed = true;
                }
                if (mPressed && keyboard.IsKeyUp(Keys.M))
                {
                    mPressed = false;
                }

                /*R: add range person*/
                if (!rPressed && keyboard.IsKeyDown(Keys.R))
                {
                    if (!IsServer)
                    {
                        //each keypress is sent as a vector for the sake of simplicity in reading data
                        // R = 2
                        client.SendLocation(new Vector2(-666, 2));
                    }
                    if (selectedGroup != null)
                    {
                        selectedGroup.AddPerson(PersonType.Range);
                        selectedGroup = null;
                    }
                    rPressed = true;
                }
                if (rPressed && keyboard.IsKeyUp(Keys.R))
                {
                    rPressed = false;
                }

                /*H: add healer person*/
                if (!hPressed && keyboard.IsKeyDown(Keys.H))
                {
                    if (!IsServer)
                    {
                        //each keypress is sent as a vector for the sake of simplicity in reading data
                        // H = 3
                        client.SendLocation(new Vector2(-666, 3));
                    }
                    if (selectedGroup != null)
                    {
                        selectedGroup.AddPerson(PersonType.Healer);
                        selectedGroup = null;
                    }
                    hPressed = true;
                }
                if (hPressed && keyboard.IsKeyUp(Keys.H))
                {
                    hPressed = false;
                }

                /*Spacebar: Clear Conversion List*/
                if (!spacePressed && keyboard.IsKeyDown(Keys.Space))
                {
                    if (!IsServer)
                    {
                        //each keypress is sent as a vector for the sake of simplicity in reading data
                        // H = 3
                        client.SendLocation(new Vector2(-666, 3));
                    }
                    if (selectedGroup != null)
                    {
                        selectedGroup.ClearInciter();
                        selectedGroup = null;
                    }
                    spacePressed = true;
                }
                if (spacePressed && keyboard.IsKeyUp(Keys.Space))
                {
                    spacePressed = false;
                }
            }

			//Mouse Clicks
			if (!mouseClicked)
			{
				if (mouse.LeftButton == ButtonState.Pressed)
				{
					//if no group is currently selected
					if (selectedGroup == null)
					{
						IGroupController clickedGroup = PeopleHolder.Instance.SelectGroup(Camera + new Vector2(mouse.X, mouse.Y), 15);
                        //Console.WriteLine("Local: " + clickedGroup.Team);
						//if a group was clicked on
						if (clickedGroup != null)
						{
							//tell the group, if the group returns true, globally select the group
							if (clickedGroup.OnClick(Camera + new Vector2(mouse.X,mouse.Y)))
							{
								selectedGroup = clickedGroup;
							}
							else //otherwise move on.
							{
								selectedGroup = null;
							}
						}
					}
					else //a group is already selected, do something with them
					{
						selectedGroup.GoalLocation(Camera + new Vector2(mouse.X, mouse.Y));
						selectedGroup = null;
					}
					mouseClicked = true;
				}
			}
			else
			{
				if (mouse.LeftButton == ButtonState.Released)
				{
                    if (!IsServer && hasStarted && this.IsActive)
                    {
                        client.SendLocation(new Vector2(Camera.X+mouse.X,Camera.Y+mouse.Y));
                        //client.SendMessage(""+(Camera.X+mouse.X)+","+(Camera.Y+mouse.Y));
                    }
					mouseClicked = false;
				}
			}
		}
    }
}
